﻿namespace ImageDupFinder.ProgramCore
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using ImageDupFinder.PluginCore;

    /// <summary>
    /// Class holding information related to single image.
    /// </summary>
    public class ImageData : IDisposable
    {
        /// <summary>
        /// File path.
        /// </summary>
        private readonly string file;

        /// <summary>
        /// Features calculated for this image.
        /// </summary>
        private readonly Dictionary<string, IFeature> features;

        /// <summary>
        /// File size in bytes.
        /// </summary>
        private long fileSize;

        /// <summary>
        /// Image dimensions.
        /// </summary>
        private Size size;

        /// <summary>
        /// Thumbnail image.
        /// </summary>
        private Image thumbnail;

        /// <summary>
        /// Initializes a new instance of the ImageData class.
        /// </summary>
        /// <param name="file">image file path</param>
        public ImageData(string file)
        {
            this.file = file;
            this.features = new Dictionary<string, IFeature>();

            this.size = new Size();
            try
            {
                System.IO.FileInfo fi = new System.IO.FileInfo(this.file);
                this.fileSize = fi.Length;
            }
            catch (UnauthorizedAccessException uae)
            {
                System.Diagnostics.Debug.Write(String.Format("File: {0}\nError: {1}", this.file, uae.ToString()));
            }
            catch (System.IO.IOException ioe)
            {
                System.Diagnostics.Debug.Write(String.Format("File: {0}\nError: {1}", this.file, ioe.ToString()));
            }
        }

        /// <summary>
        /// Finalizes an instance of the ImageData class.
        /// </summary>
        ~ImageData()
        {
            this.Dispose();
        }

        /// <summary>
        /// Gets file associated with this image.
        /// </summary>
        public string File
        {
            get { return this.file; }
        }

        /// <summary>
        /// Gets collection of calculated features.
        /// </summary>
        public Dictionary<string, IFeature> Features
        {
            get { return this.features; }
        }

        /// <summary>
        /// Gets image resolution (in pixels)
        /// </summary>
        public Size Size
        {
            get { return this.size; }
        }

        /// <summary>
        /// Gets file size (in bytes)
        /// </summary>
        public long FileSize
        {
            get { return this.fileSize; }
        }

        /// <summary>
        /// Gets or sets thumbnail image.
        /// </summary>
        public Image Thumbnail
        {
            get { return this.thumbnail; }
            set { this.thumbnail = value; }
        }

        /// <summary>
        /// Calculate image features
        /// </summary>
        /// <param name="featuresCollections">Collection of features to calculate for this image</param>
        public void CalculateFeatures(List<IFeatureCollection> featuresCollections)
        {
            try
            {
                // load image from file
                Bitmap image = (Bitmap)Bitmap.FromFile(this.file);

                // set image size metadata
                this.size.Height = image.Height;
                this.size.Width = image.Width;

                // calculate features
                foreach (IFeatureCollection c in featuresCollections)
                {
                    string[] fl = c.FeatureList;

                    // check if features are already calculated
                    if (fl.Length > 0 && !this.features.ContainsKey(fl[0]))
                    {
                        IFeature[] tmp = c.CalculateFeatures(image);
                        foreach (IFeature f in tmp)
                        {
                            this.features.Add(f.Name, f);
                        }
                    }
                }

                image.Dispose();
            }
            catch (OutOfMemoryException ome)
            {
                System.Diagnostics.Debug.Write(String.Format("File: {0}\nError: {1}", this.file, ome.ToString()));
            }
            catch (System.IO.FileNotFoundException fnfe)
            {
                System.Diagnostics.Debug.Write(String.Format("File: {0}\nError: {1}", this.file, fnfe.ToString()));
            }
        }

        /// <summary>
        /// Calculates similarity between this image and other image
        /// </summary>
        /// <param name="other">image to calculate similarity with</param>
        /// <param name="method">feature name used to get similarity</param>
        /// <returns>similarity in range 0-1.
        /// "0" means images are identical.
        /// "1 or more" means images have nothing in common.</returns>
        /// <exception cref="System.ArgumentNullException"/>
        public float CalcualteSimilarity(ImageData other, string method)
        {
            if (other == null || method == null)
            {
                throw new ArgumentNullException();
            }

            IFeature feature = null;
            IFeature otherFeature = null;
            if (features.TryGetValue(method, out feature) && other.Features.TryGetValue(method, out otherFeature))
            {
                return feature.GetSimilarity(otherFeature);
            }

            return 1.0f;
        }

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by this ImageData.
        /// </summary>
        public void Dispose()
        {
            if (this.thumbnail != null)
            {
                this.Thumbnail.Dispose();
            }
        }

        #endregion
    }
}
